/** <!--------------------------------------------------------------------------------------------->
 * @file XXXXXXX.XXX
 *
 * GSvn - A Graphical Client for SVN
 * Copyright (C) 2012  Karl Krach, gsvn@bluespirit.la
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 <!---------------------------------------------------------------------------------------------> */


#include "GraphBranch.h"


GraphBranch::GraphBranch( const QString& strName ) : m_strName(strName), m_fWidth(0), m_fHeight(0),
    m_pRefItem(0), m_fNextItemTop(S_MARGIN_TOP), m_iFirstItem(0), m_iLineStart(0)
{
	setFlag( QGraphicsItem::ItemIsFocusable, true );
	setFlag( QGraphicsItem::ItemIsMovable, false );
}


void GraphBranch::SetHeight( float fHeight )
{
	m_fHeight = fHeight;
	RearrangeItems();
}


void GraphBranch::GeometryHasChanged()
{
	float fWidth = 0;
	foreach( GraphItem* pItem, m_aRevisions )
	{
		if( pItem->isVisible() && pItem->DrawGraph() && pItem->logicalRect().width() + s_iBorder*2 > fWidth )
		{
			qDebug() << "A: " << pItem->GetName() << fWidth << " -> " << (pItem->logicalRect().width() + s_iBorder*2);
			fWidth = pItem->logicalRect().width() + s_iBorder*2;
		}
	}
	if( m_fWidth != fWidth )
	{
		qDebug() << "B: " << m_fWidth << " -> " << fWidth;
		prepareGeometryChange();
		m_fWidth = fWidth;
	}
	RearrangeItems();
}


bool GraphBranch::Scroll( int iSteps )
{
	if( m_iFirstItem-iSteps < 0 )
	{
		if( m_iFirstItem == 0 )
			return false;
		m_iFirstItem = 0;
	}
	else m_iFirstItem -= iSteps;

	prepareGeometryChange();
	return RearrangeItems();
}

QRectF GraphBranch::boundingRect() const
{
	QGraphicsScene* pScene = scene();
	if( !pScene ) return QRectF();

	return QRectF( m_fWidth / -2.0, 0,
	               m_fWidth + 10, m_fNextItemTop
	              );
}

void GraphBranch::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);

	painter->save();

	painter->setPen( QColor( Qt::blue ) );
	painter->setBrush( Qt::NoBrush );
	QPointF oTopLeft(m_fWidth / -2.0, 0);
	QPointF oBottomRight(m_fWidth / 2.0 + 10, m_fNextItemTop < m_fHeight ? m_fNextItemTop : m_fHeight );
	painter->drawLine( 0, m_iLineStart, 0, oBottomRight.y() );

	painter->setPen( Qt::NoPen );
	painter->setBrush( QBrush( QColor(Qt::blue), Qt::FDiagPattern ) );
	painter->drawRect( QRectF(oTopLeft, oBottomRight) );

	painter->setPen( QColor(Qt::black) );
	painter->drawText( QRectF(oTopLeft, oBottomRight), Qt::AlignHCenter, m_strName );

	painter->restore();
}

QVariant GraphBranch::itemChange( GraphicsItemChange eChange, const QVariant& oValue )
{
	switch( eChange )
	{
		case QGraphicsItem::ItemChildAddedChange:
			{
				QGraphicsItem* pItem = oValue.value<QGraphicsItem*>();
				GraphItem* pGraphItem = dynamic_cast<GraphItem*>(pItem);

				if( pGraphItem != NULL )
				{
					m_aRevisions << pGraphItem;
					if( pGraphItem->logicalRect().width() + s_iBorder*2 > m_fWidth )
					{
						prepareGeometryChange();
						m_fWidth = pGraphItem->logicalRect().width() + s_iBorder*2;
					}
					float fTop = pGraphItem->logicalRect().y();
					pGraphItem->moveBy( 0, m_fNextItemTop - fTop );
					m_fNextItemTop += pGraphItem->logicalRect().height() + 10;
				}
				else
				{
					qWarning() << "GraphBranch: Cannot handle non GraphItem objects!";
				}
			}
			break;
		case QGraphicsItem::ItemChildRemovedChange:
		{
			QGraphicsItem* pItem = oValue.value<QGraphicsItem*>();
			GraphItem* pGraphItem = dynamic_cast<GraphItem*>(pItem);

			if( pGraphItem != NULL )
			{
				m_aRevisions.remove( m_aRevisions.indexOf(pGraphItem) );
			}
		}
			break;
		default:
			// Do nothing
			break;
	}
	return QGraphicsItem::itemChange( eChange, oValue );
}


bool GraphBranch::RearrangeItems()
{
	const int IN_ITEMS = 5;
	const int OUT_ITEMS = 5;

	bool bRetVal   = false;
	int iMaxHeight = 0;
	m_fNextItemTop = S_MARGIN_TOP;
	int iLastItem  = INT_MAX;
	m_iLineStart   = -1;

	for( int iCnt=0; iCnt < m_aRevisions.count(); iCnt++ )
	{
		GraphItem* pItem = m_aRevisions.at(iCnt);
		if( iMaxHeight < pItem->logicalRect().height() )
			iMaxHeight = pItem->logicalRect().height();

		//
		// Hidden entries at the start
		//
		if( iCnt < m_iFirstItem-IN_ITEMS )
		{
			if( m_iLineStart == -1 && pItem->DrawGraph() )
			{
				m_iLineStart = S_MARGIN_TOP;
//				qDebug() << "A Setting line start to " << m_iLineStart << " for " << GetName();
			}
			pItem->setVisible( false );
			pItem->setEnabled( false );
		}
		//
		// Stacked entries at the start
		//
		else if( iCnt < m_iFirstItem )
		{
			if( m_iLineStart == -1 && pItem->DrawGraph() )
			{
				m_iLineStart = S_MARGIN_TOP;
//				qDebug() << "B Setting line start to " << m_iLineStart << " for " << GetName();
			}
			bRetVal = true;
			int iInCount = iCnt - m_iFirstItem;		// from IN_ITEMS to 1
			pItem->setVisible( true );
			pItem->setEnabled( false );

			const int iNewXPos = iInCount * -2;
			const int iNewYPos = m_fNextItemTop + pItem->logicalRect().height()/2  + iInCount*8;
			pItem->setPos( iNewXPos, iNewYPos );
			pItem->setZValue( iCnt );
			pItem->setOpacity( 1.0 - (double)-iInCount / IN_ITEMS / 4 );
		}
		//
		// Regular and visible content
		//
		else if( iCnt < iLastItem )
		{
			if( m_iLineStart == -1 && pItem->DrawGraph() )
			{
				m_iLineStart = m_fNextItemTop + pItem->logicalRect().height();
//				qDebug() << "C Setting line start to " << m_iLineStart << " for " << GetName();
			}

			bRetVal = true;
			pItem->setVisible( true );
			pItem->setEnabled( true );

			if( iCnt == m_iFirstItem )
			{
				pItem->setEnabled( false );
			}

			const int iNewXPos = 0;
			const int iNewYPos = m_fNextItemTop + pItem->logicalRect().height()/2;
			pItem->setPos( iNewXPos, iNewYPos );
			pItem->setZValue( iCnt );
			pItem->setOpacity( 1 );

			m_fNextItemTop += pItem->logicalRect().height() + 10;

			if( m_fNextItemTop+iMaxHeight+30 >= m_fHeight )
			{
				pItem->setEnabled( false );
				iLastItem = iCnt;
			}

		}
		else if( iCnt < iLastItem+OUT_ITEMS+1 )
		{
			bRetVal = true;
			GraphItem* pLastItem = m_aRevisions.at(iLastItem);

			int iOutCount = iCnt - iLastItem;		// from 1 to OUT_ITEMS
			pItem->setVisible( true );
			pItem->setEnabled( false );

			const int iNewXPos = iOutCount * 2;
			const int iNewYPos = pLastItem->pos().y() + iOutCount*6;
			pItem->setPos( iNewXPos, iNewYPos );
			pItem->setZValue( iLastItem - iOutCount );
			pItem->setOpacity( 1 - (double)iOutCount / OUT_ITEMS / 4 );

			m_fNextItemTop += iOutCount*6;
		}
		else
		{
			pItem->setVisible( false );
			pItem->setEnabled( false );
		}
	}
	return bRetVal;
}



